package com.humingzhang.client.ftp;

import com.humingzhang.client.FileClient;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
 * FTP服务器
 *
 * @author minghu.zhang
 */
public class FtpFileClient implements FileClient {
    private static final String SLASH = "/";
    private static String LOCAL_CHARSET = "GBK";
    private static final String SERVER_CHARSET = "ISO-8859-1";
    private final FTPClient client;
    /**
     * ftp配置
     */
    private final FtpConfig ftpConfig;

    public FtpFileClient(FtpConfig ftpConfig) {
        this.ftpConfig = ftpConfig;
        try {
            client = connect();
        } catch (IOException e) {
            throw new RuntimeException("FTP连接失败," + e.getMessage(), e);
        }
    }

    /***
     * 上传文件
     */
    @Override
    public String upload(String path, InputStream in) throws IOException {
        if (path.startsWith(SLASH)) {
            createPath(client, ftpConfig.getRootPath() + path.substring(0, path.lastIndexOf("/")));
        }

        if (path.startsWith(SLASH)) {
            path = ftpConfig.getRootPath() + path;
        } else {
            path = ftpConfig.getRootPath() + SLASH + path;
        }

        String ftpPath = new String(path.getBytes(LOCAL_CHARSET), SERVER_CHARSET);

        FTPFile[] ftpFiles = client.listFiles(ftpPath);
        if (ftpFiles.length > 0) {
            throw new RuntimeException("文件已存在," + path);
        }
        client.storeUniqueFile(ftpPath, in);
        return path;
    }

    /**
     * 下载文件
     */
    @Override
    public void download(String path, OutputStream output) throws IOException {
        String ftpPath = getExistFilePath(path, client);
        client.retrieveFile(ftpPath, output);
    }

    /**
     * 删除文件
     */
    @Override
    public void delete(String path) throws IOException {
        String ftpPath = getExistFilePath(path, client);
        client.deleteFile(ftpPath);
    }

    @Override
    public void close() {
        try {
            if (client != null) {
                client.abort();
                client.disconnect();
            }
        } catch (IOException ignored) {

        }
    }

    /**
     * 获取待修改的路径
     */
    private String getExistFilePath(String path, FTPClient client) throws IOException {
        if (path.startsWith(SLASH)) {
            path = ftpConfig.getRootPath() + path;
        } else {
            path = ftpConfig.getRootPath() + SLASH + path;
        }
        String ftpPath = new String(path.getBytes(LOCAL_CHARSET), SERVER_CHARSET);
        FTPFile[] ftpFiles = client.listFiles(ftpPath);
        if (ftpFiles.length == 0) {
            throw new FileNotFoundException("文件不存在," + path);
        }
        return ftpPath;
    }

    /**
     * 连接到ftp服务器
     */
    private FTPClient connect() throws IOException {
        FTPClient client = new FTPClient();
        if (ftpConfig.getPort() == null) {
            ftpConfig.setPort(21);
        }

        client.connect(ftpConfig.getHost(), ftpConfig.getPort());
        setLocalEncoding(client);

        client.enterLocalPassiveMode();
        client.setFileType(FTPClient.BINARY_FILE_TYPE);
        
        login(client);

        //创建根目录
        if (ftpConfig.getRootPath() != null && !"".equals(ftpConfig.getRootPath())) {
            createPath(client, ftpConfig.getRootPath());
        }

        return client;
    }

    /**
     * ftp登录
     */
    private void login(FTPClient client) throws IOException {
        //若不设置用户名则使用匿名登录
        if (ftpConfig.getUsername() == null || "".equals(ftpConfig.getUsername().trim())) {
            client.login("Anonymous", "");
        } else {
            client.login(ftpConfig.getUsername(), ftpConfig.getPassword());
        }
    }

    /**
     * 设置本地编码格式
     */
    private void setLocalEncoding(FTPClient client) throws IOException {
        String optsUtf8 = "OPTS UTF8";
        String utfOn = "ON";
        if (FTPReply.isPositiveCompletion(client.sendCommand(optsUtf8, utfOn))) {
            // 开启服务器对UTF-8的支持，如果服务器支持就用UTF-8编码，否则就使用本地编码（GBK）.
            LOCAL_CHARSET = "UTF-8";
        }
        client.setControlEncoding(LOCAL_CHARSET);
    }

    /**
     * 创建多级目录
     */
    private void createPath(FTPClient client, String path) throws IOException {
        if (path == null || !path.startsWith(SLASH)) {
            throw new RuntimeException("路径需要以/开头");
        }
        String ftpPath = new String(path.getBytes(LOCAL_CHARSET), SERVER_CHARSET);

        String[] directoryList = ftpPath.split(SLASH);
        StringBuilder builder = new StringBuilder();
        for (String directory : directoryList) {
            if (!"".equals(directory)) {
                builder.append(SLASH).append(directory);
                String tempPath = builder.toString();
                client.mkd(tempPath);
                client.cwd(tempPath);
            }
        }
    }
}
